/*
 *  Copyright Beijing 58 Information Technology Co.,Ltd.
 *
 *  Licensed to the Apache Software Foundation (ASF) under one
 *  or more contributor license agreements.  See the NOTICE file
 *  distributed with this work for additional information
 *  regarding copyright ownership.  The ASF licenses this file
 *  to you under the Apache License, Version 2.0 (the
 *  "License"); you may not use this file except in compliance
 *  with the License.  You may obtain a copy of the License at
 *
 *        http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing,
 *  software distributed under the License is distributed on an
 *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 *  KIND, either express or implied.  See the License for the
 *  specific language governing permissions and limitations
 *  under the License.
 */
package com.eclub.emotion.rootstone.protocol;

import java.io.IOException;

import com.eclub.emotion.rootstone.protocol.enumeration.SDPType;
import com.eclub.emotion.rootstone.protocol.sdp.ProtocolStruct;
import com.eclub.emotion.rootstone.protocol.secure.DESCoderHelper;
import com.eclub.emotion.rootstone.serializer.io.RsProtocolException;
import com.eclub.emotion.rootstone.serializer.io.Serializer;
import com.eclub.emotion.rootstone.util.ByteUtility;

/**
 * 
 * First version protocol definition 
 * 
 * 1byte(Version No.) | 4byte(Protocol total length) | 4byte(Serial Number) | 1byte(Service No.) | 1byte(Message Body Type) | 1byte(Compress algorithm) | 1byte (serialization Rule)| 1byte 平台(.net java ...) | n byte消息体 | 5byte(分界符)
 *    0                1~4              5~8              9                   10                   11                      12                     13
 * 消息头总长度:14byte 
 * 
 * 协义总长度 = 消息头总长度 + 消息体总长度 (不包括分界符)
 * 
 * 尾分界符: 9, 11, 13, 17, 18
 * 
 * 版本号从ASCII > 48 开始标识
 * 
 */
public class Protocol {

    public static final byte VERSION = 1;
    private int totalLen;
    private int sessionID;
    private byte serviceID;
    private SDPType sdpType;
    private byte[] userData;
    private Object sdpEntity;

	/**
	 * 协义头14个byte
	 */
	private final static int HEAD_STACK_LENGTH = 14;

    public Protocol(int sessionId, 
    		byte serviceId,
    		SDPType sdpType,
    		Object sdpEntity) {
    	
            this.sdpEntity = sdpEntity;
            this.sessionID = sessionId;
            this.serviceID = serviceId;
            this.sdpType = sdpType;
    }
    
    public Protocol(int sessionId, 
    		byte serviceId,
    		SDPType sdpType, 
    		byte[] userData) {
    	
            this.userData = userData;
            this.sessionID = sessionId;
            this.serviceID = serviceId;
            this.sdpType = sdpType;
    }
    

    public Protocol() {
    	
    }
    
    public int getVersion() {
    	return VERSION;
    }

    public int getTotalLen() {
        return totalLen;
    }

    public void setTotalLen(int totalLen) {
        this.totalLen = totalLen;
    }


    public byte getServiceID() {
        return serviceID;
    }

    public void setServiceID(byte serviceID) {
        this.serviceID = serviceID;
    }

    public int getSessionID() {
        return sessionID;
    }

    public void setSessionID(int sessionID) {
        this.sessionID = sessionID;
    }

    public byte[] getUserData() {
        return userData;
    }

    public void setUserData(byte[] userData) {
        this.userData = userData;
    }

    public SDPType getSDPType() {
        return sdpType;
    }

    public void setSDPType(SDPType sdpType) {
        this.sdpType = sdpType;
    }

    public void setSdpEntity(Object sdpEntity) {
		this.sdpEntity = sdpEntity;
	}

	public Object getSdpEntity() {
		return sdpEntity;
	}
	
	public byte getCompressType(){
		return 0;
	}
	
	public byte getSerializeType(){
		return 0;
	}
	
	public byte getPlatformType(){
		return 0;
	}
	

	public byte[] toBytes() throws Exception{
        int startIndex = 0;
       
        
        this.sdpType = SDPType.getSDPType(this.sdpEntity);
        byte[] sdpData =  Serializer.getInstance().serialize(this.sdpEntity);
        
        int protocolLen = HEAD_STACK_LENGTH + sdpData.length;
        this.setTotalLen(protocolLen);
        
        byte[] data = new byte[protocolLen];
        data[0] = Protocol.VERSION;

        startIndex += ProtocolStruct.Version;
        System.arraycopy(ByteUtility.intToBytesLittleEndian(this.getTotalLen()), 0, data, startIndex, ProtocolStruct.TotalLen);

        startIndex += ProtocolStruct.TotalLen;
        System.arraycopy(ByteUtility.intToBytesLittleEndian(this.getSessionID()), 0, data, startIndex, ProtocolStruct.SessionId);

        startIndex += ProtocolStruct.SessionId;
        data[startIndex] = this.getServiceID();

        startIndex += ProtocolStruct.ServerId;
        data[startIndex] = (byte) this.getSDPType().getNum();

        startIndex += ProtocolStruct.SDPType;
        data[startIndex] = (byte) this.getCompressType();

        startIndex += ProtocolStruct.CompressType;
        data[startIndex] = (byte) this.getSerializeType();

        startIndex += ProtocolStruct.SerializeType;
        data[startIndex] = (byte) this.getPlatformType();

        startIndex += ProtocolStruct.Platform;
        System.arraycopy(sdpData, 0, data, startIndex, protocolLen - startIndex);

        return data;
    }
	
    public static Protocol fromBytes(byte[] data) throws Exception  {
        Protocol p = new Protocol();
        int startIndex = 0;
        if(data[startIndex] != Protocol.VERSION) {
        	throw new RsProtocolException("incorrect protocol version");
        }

        startIndex += ProtocolStruct.Version;//1
        byte[] totalLengthByte = new byte[ProtocolStruct.TotalLen];
        for (int i = 0; i < ProtocolStruct.TotalLen; i++) {
            totalLengthByte[i] = data[startIndex + i];
        }
        p.setTotalLen(ByteUtility.bytesToIntLittleEndian(totalLengthByte));

        startIndex += ProtocolStruct.TotalLen;//5
        byte[] sessionIDByte = new byte[ProtocolStruct.SessionId];
        for (int i = 0; i < ProtocolStruct.SessionId; i++) {
            sessionIDByte[i] = data[startIndex + i];
        }
        p.setSessionID(ByteUtility.bytesToIntLittleEndian(sessionIDByte));

        startIndex += ProtocolStruct.SessionId;//9
        p.setServiceID(data[startIndex]);

        startIndex += ProtocolStruct.ServerId;//10
        p.setSDPType(SDPType.getSDPType(data[startIndex]));

        startIndex += ProtocolStruct.SDPType;

        startIndex += ProtocolStruct.CompressType;
       
        startIndex += ProtocolStruct.SerializeType;

        startIndex += ProtocolStruct.Platform;

        byte[] sdpData = new byte[data.length - startIndex];
        System.arraycopy(data, startIndex, sdpData, 0, data.length - startIndex);
        p.setUserData(sdpData);
        
        
        p.setSdpEntity(Serializer.getInstance().deserialize(sdpData, SDPType.getSDPClass(p.getSDPType())));
        return p;
    }
    
    /**
     * 
     * @param data encryption data
     * @param rights 
     * @param desKey Decryption key
     * @return
     * @throws Exception
     */
    public static Protocol fromBytes(byte[] data, boolean rights, byte[] desKey) throws Exception {
        Protocol p = new Protocol();
        int startIndex = 0;
        if(data[startIndex] != Protocol.VERSION) {
        	throw new IOException("incorrect protocol version");
        }

        startIndex += ProtocolStruct.Version;//1
        byte[] totalLengthByte = new byte[ProtocolStruct.TotalLen];
        for (int i = 0; i < ProtocolStruct.TotalLen; i++) {
            totalLengthByte[i] = data[startIndex + i];
        }
        p.setTotalLen(ByteUtility.bytesToIntLittleEndian(totalLengthByte));

        startIndex += ProtocolStruct.TotalLen;//5
        byte[] sessionIDByte = new byte[ProtocolStruct.SessionId];
        for (int i = 0; i < ProtocolStruct.SessionId; i++) {
            sessionIDByte[i] = data[startIndex + i];
        }
        p.setSessionID(ByteUtility.bytesToIntLittleEndian(sessionIDByte));

        startIndex += ProtocolStruct.SessionId;//9
        p.setServiceID(data[startIndex]);

        startIndex += ProtocolStruct.ServerId;//10
        p.setSDPType(SDPType.getSDPType(data[startIndex]));

        startIndex += ProtocolStruct.SDPType;

        startIndex += ProtocolStruct.CompressType;

        startIndex += ProtocolStruct.SerializeType;

        startIndex += ProtocolStruct.Platform;

        byte[] sdpData = new byte[data.length - startIndex];
        System.arraycopy(data, startIndex, sdpData, 0, data.length - startIndex);
        
        //data decryption
        if(p.getSDPType().getNum() != 5 && rights && desKey !=null){
        	sdpData = DESCoderHelper.getInstance().decrypt(sdpData, desKey);
        }
        
        p.setUserData(sdpData);
        p.setSdpEntity(Serializer.getInstance().deserialize(sdpData, SDPType.getSDPClass(p.getSDPType())));
        return p;
    }
    
    /**
     * 
     * @param rights
     * @param desKey
     * @return
     * @throws Exception
     */
    public byte[] toBytes(boolean rights, byte [] desKey) throws Exception{
        int startIndex = 0;
        
        this.sdpType = SDPType.getSDPType(this.sdpEntity);
        byte[] sdpData = Serializer.getInstance().serialize(this.sdpEntity);
        
        //data encrypt
        if(this.getSDPType().getNum() != 5 && rights && desKey != null){
        	sdpData = DESCoderHelper.getInstance().encrypt(sdpData, desKey);
        }
        
        int protocolLen = HEAD_STACK_LENGTH + sdpData.length;
        this.setTotalLen(protocolLen);
        
        byte[] data = new byte[protocolLen];
        data[0] = Protocol.VERSION;

        startIndex += ProtocolStruct.Version;
        System.arraycopy(ByteUtility.intToBytesLittleEndian(this.getTotalLen()), 0, data, startIndex, ProtocolStruct.TotalLen);

        startIndex += ProtocolStruct.TotalLen;
        System.arraycopy(ByteUtility.intToBytesLittleEndian(this.getSessionID()), 0, data, startIndex, ProtocolStruct.SessionId);

        startIndex += ProtocolStruct.SessionId;
        data[startIndex] = this.getServiceID();

        startIndex += ProtocolStruct.ServerId;
        data[startIndex] = (byte) this.getSDPType().getNum();

        startIndex += ProtocolStruct.SDPType;
        data[startIndex] = (byte) this.getCompressType();

        startIndex += ProtocolStruct.CompressType;
        data[startIndex] = (byte) this.getSerializeType();

        startIndex += ProtocolStruct.SerializeType;
        data[startIndex] = (byte) this.getPlatformType();

        startIndex += ProtocolStruct.Platform;
        System.arraycopy(sdpData, 0, data, startIndex, protocolLen - startIndex);

        return data;
    }
    
}